package com.dqx.opengl_es_demo.triangle2;

import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * 渲染器（相当于画笔）
 */
public class TriangleRenderer2 implements GLSurfaceView.Renderer {
    //顶点着色器代码
    private String vertexShaderCode =
            "attribute vec4 vPosition;\n" +//定义一个坐标向量
                    "uniform mat4 vMatrix;\n" +//定义一个变化矩阵
                    "void main() {\n" +
                    "    gl_Position = vMatrix*vPosition;\n" +//gl_Position顶点坐标：通过矩阵乘于坐标向量得到
                    "}";
    //片元着色器代码
    private String fragmentShaderCode =
            "precision mediump float;\n" +
                    " uniform vec4 vColor;\n" +//定义一个颜色属性
                    " void main() {\n" +
                    "     gl_FragColor = vColor;\n" +//gl_FragColor片元颜色
                    " }";

    //顶点坐标
    float vertexCoords[] = {
            0.5f, 0.5f, 0.0f, // 三角形上面点坐标
            -0.5f, -0.5f, 0.0f, //三角形左边点坐标
            0.5f, -0.5f, 0.0f  // 三角形右边点坐标
    };

    //片元颜色RGBA
    float color[] = {
            1.0f, 1.0f, 1.0f, 1.0f
    };

    //浮点型占4个字节长度
    int FLOAT_BYTE_LENGTH = 4;

    //顶点个数（数组中每3个数据代表一个顶点坐标）
    int vertexCount = vertexCoords.length / 3;

    //缓冲区对象（OpenGL不能直接操作数组，需要将数组转成FloatBuffer等数据类型）
    FloatBuffer vertexStride;

    //程序id
    int program;

    //顶点着色器id
    int vertexShader;

    //片元着色器id
    int fragmentShader;

    //顶点位置句柄
    int mPositionHandle;

    //片元颜色句柄
    int mColorHandle;

    //相机矩阵
    private float[] mViewMatrix = new float[16];

    //投影矩阵
    private float[] mProjectMatrix = new float[16];

    //变化矩阵
    private float[] mMVPMatrix = new float[16];

    //变化矩阵句柄
    private int mMatrixHandler;

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //设置背景
        GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        //初始化缓冲区对象
        vertexStride = initFloatBuffer(vertexCoords);
        //创建程序
        program = creatProgram();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        //设置视图窗口的位置及大小
        GLES20.glViewport(0, 0, width, height);

        //宽高比
        float ratio = (float) width / height;

        float left = -ratio;//相对观察点near面的左边距
        float right = ratio;//相对观察点near面的右边距
        float bottom = -1;//相对观察点near面的下边距
        float top = 1;//相对观察点near面的上边距

        // near和far需要结合拍摄相机即观察者眼睛的位置来设置，
        // 例如setLookAtM中设置eyeX = 0, eyeY = 0, eyeZ = 7，
        // near设置的范围需要小于7才可以看得到绘制的图像，
        // 如果大于7，图像就会处于了观察者眼睛的后面，
        // 这样绘制的图像就会消失在镜头前。
        // far一定比near大，一般会设置得比较大，
        // 如果设置的比较小，一旦3D图形尺寸很大，
        // 这时候由于far太小，这个投影矩阵没法容纳图形全部的背面，
        // 这样3D图形的背面会有部分隐藏掉的
        float near = 3;//相对观察点near面距离
        float far = 7;//相对观察点far面距离
        //设置透视投影矩阵
        Matrix.frustumM(mProjectMatrix, 0, left, right, bottom, top, near, far);

        float eyeX = 0.0f;//相机位置x坐标
        float eyeY = 0.0f;//相机位置y坐标
        float eyeZ = 7.0f;//相机位置z坐标
        float centerX = 0.0f;//相机观察的目标点x坐标
        float centerY = 0.0f;//相机观察的目标点y坐标
        float centerZ = 0.0f;//相机观察的目标点z坐标
        float upX = 0.0f;//相机UP向量X分量
        float upY = 1.0f;//相机UP向量Y分量
        float upZ = 0.0f;//相机UP向量Z分量
        //设置相机位置
        Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);

        //计算变换矩阵 = 投影矩阵* 相机矩阵
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectMatrix, 0, mViewMatrix, 0);
    }



    @Override
    public void onDrawFrame(GL10 gl) {
        //清除深度缓存和颜色缓存
        GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
        //绘制
        draw();
    }

    /**
     * 初始化缓冲区对象
     *
     * @param buffer
     * @return
     */
    private FloatBuffer initFloatBuffer(float[] buffer) {
        FloatBuffer fb = ByteBuffer.allocateDirect(buffer.length * FLOAT_BYTE_LENGTH)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(buffer);
        fb.position(0);
        return fb;
    }

    /**
     * 创建着色器，并返回着色器对象id
     *
     * @param type       着色器类型
     * @param shaderCode 着色器代码
     * @return
     */
    private int loadShader(int type, String shaderCode) {
        //创建着色器对象
        int shader = GLES20.glCreateShader(type);
        //加载着色器代码源
        GLES20.glShaderSource(shader, shaderCode);
        //编译着色器
        GLES20.glCompileShader(shader);
        return shader;
    }

    /**
     * 创建程序
     */
    private int creatProgram() {
        //程序对象id
        int program = 0;
        //创建顶点着色器
        vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
        //创建片元着色器
        fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
        //创建程序对象
        program = GLES20.glCreateProgram();
        //将顶点着色器加到程序中
        GLES20.glAttachShader(program, vertexShader);
        //将片段着色器加到程序中
        GLES20.glAttachShader(program, fragmentShader);
        //连接着色器
        GLES20.glLinkProgram(program);
        return program;
    }

    /**
     * 绘制
     */
    public void draw() {
        //使用程序
        GLES20.glUseProgram(program);

        //获取变换矩阵vMatrix属性句柄
        mMatrixHandler= GLES20.glGetUniformLocation(program,"vMatrix");
        //设置vMatrix的值
        GLES20.glUniformMatrix4fv(mMatrixHandler,1,false,mMVPMatrix,0);

        //获取顶点着色器的vPosition属性句柄
        mPositionHandle = GLES20.glGetAttribLocation(program, "vPosition");
        //启用三角形位置属性的句柄
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        //设置vPosition的值
        // 第1个参数：要赋值的属性句柄。
        // 第2个参数：数组中每3个数表示一个顶点坐标。
        // 第3个参数：数据类型。
        // 第4个参数：是否标准化。
        // 第5个参数：每个顶点坐标所占字节长度。
        // 第6个参数：缓冲区对象。
        GLES20.glVertexAttribPointer(mPositionHandle, 3,
                GLES20.GL_FLOAT, false,
                3 * FLOAT_BYTE_LENGTH, vertexStride);

        //获取片元着色器的vColor属性句柄
        mColorHandle = GLES20.glGetUniformLocation(program, "vColor");
        //设置vColor的值
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);

        //绘制三角形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
    }

    /**
     * 释放着色器资源
     */
    private void relese() {
        GLES20.glDeleteShader(vertexShader);
        GLES20.glDeleteShader(fragmentShader);
        GLES20.glDeleteProgram(program);
    }
}
